/* Online Java - IDE, Code Editor, Compiler Online Java is a quick and easy tool that helps you to build, compile, test your programs online. */ // This has been created to ensure I can utilize any random functions more efficiently. // It is a creation of the NcR combinations calculator. // It has used techniques I learnt including recursion and also memoization to speed up execution. // I will incorporate this into Java applications I created previously.. //ISSUES: // THIS HAS BEEN CREATED USING n = 3 and r =3 // IT HAS NO ISSUES WITH THESE SET. // THE FIRST PART WILL BE TO TRY AND ACCOMODATE FOR A LARGER n=4 and r=4 // HENCE I HAVE INCLUDED ONE COMMENTED SWITCH STATEMENT ALSO // I CAN THEN TRY TO SEE WHAT HAPPENS IF R is reduced // UNLIKE OTHER PROBLEMS LIKE GENERATING AND CHAINING A RANDOM NUMBER INTO BIGGER ONE, // THERE IS A BIT MORE TECHNICAL PROCESSING TO TRUNCATE THE SET ENTRY AND PERFORM CONVERSIONS FOR CURRENCIES... // IT IS ALSO OPEN TO INTRODUCING TRANSACTION FEES IN THE SWITCH STATEMENT // HOWEVER THIS IS NOT PART OF THE PROBLEM // I HAVE NOW ADJUSTED SO THAT IT DOES NOT SHOW CONVERSIONS IF THE SET HAS ALREADY BEEN PROCESSED. // NEED TO REMEMBER SAMPLE IS R AND OBJECTS IS N.... // R IS SMALLER THAN N // Tried following... its largely successful... not sure how to get any closer.... //r=2 n=3 PASS //r=4 n=4 PASS //r=2 n=4 PASS //r=3 n=4 PASS //r=2 n=2 FAILS (it processes n currencies) //r=3 n=3 FAILS (it processes n currencies) //r=1 n=2 PASS (THE CODE BREAKS SINCE ONLY ONE CURRENCY, NEED TO PROMPT END USER) //r=1 n=1 FAILS (it processes n currencies) //r=4 n=4 FAIL //r=2 n=5 PASS //r=3 n=5 PASS //r=4 n=5 PASS //r=5 n=5 PASS // ALL FIXED....!!! import java.math.*; import java.util.*; import java.math.RoundingMode; // used for round to 2dp import java.text.DecimalFormat; // used for formatting number class conversions { public conversions () { } } class exchangeCombinations { DecimalFormat format = new DecimalFormat("##0. 00"); //format to 2dp.. String currencyExchange=""; // this is used to hold chain of exchanges double exchangeRate; // this is exchange rate double amount=50.0; //amount to start transactions double currency; // hold values of amount * exchangeRate; //String temp1; // this holds the value of numberArray, which is the symbol int k; // this will hold offset value of 4 for delimiter... £=>$ in context of substring to represent this String fullConversionsReverse; // this will hold ALL forward currency exchanges in reverse String exchangeArbitrageFlipped; // it holds conversion reverse direction as such £=>$ String outcome=""; // this will hold the exchange undertaken in format £X.XX Double ArbitrageChecker; // this holds difference between initial amount and final amount (reverse conversion) String fullConversions; // opposite fullConversionsReverse. ALL Transactions in forward direction.. String exchangeArbitrage; // it holds the conversion such as £=>$. It is based on fullConversions String newCurrency; // this is the currency that it will be exchanged into. String initialAmount; // This can be changed at any point of conversions. Useful if starting currency is too small long combinations; // all combinations from NcR calculator. int count; //number cycles undertaken. Used to give indication of executions to store combinations in Set... String temp; // used to hold currency symbol StringJoiner sj = new StringJoiner("=>"); // all currencies will be added and get => as a seperator Set <String> s = new HashSet <>(); // this will store the combinations int startPos; // used in arbitrageCheckerTwoCurrencies and arbitrageCheckerTwoCurrencies to traverse fullConversions int endPos; // used in arbitrageCheckerTwoCurrencies and arbitrageCheckerTwoCurrencies to traverse fullConversions List <String> lst = new ArrayList<>(Arrays.asList("$","£","€","¥","F")); // this is list of currencies. In future, it will be extended. List <String> copy = new ArrayList<>(lst); //keeps a copy // this list keeps a copy since during program execution the top list is modified StringBuilder sb; // used for fullConversions string... And then to use the reverse method for fullConversionsReverse int currentSetSize; // used to check if Set incremented, otherwise no point demonstrating screen output again boolean exitCondition; // used in do while loop... the exit point varies depending on scenario int n; // total number of Objects selected int r; // total sample size selected int differenceObjectsAndTotalCurrenciesAvailable; // used if n chosen differs to n objects available. int randomNumber; // random number generated.. This will be zero index based. String currencySymbol=""; // value at index of randomNumber in the set String conversion; //this is chain of conversion int totalCurrenciesAvailable; // this is same value as size of list Random rand = new Random(); // random seed used to generate random number public String currencyExchange(String exchangeCurrencies, String currencySymbol) { // this holds the exchange rates and calculate new currency. switch (exchangeCurrencies) { case "F=>¥": exchangeRate=174.59; currency = amount*exchangeRate; break; case "F=>£": exchangeRate=0.89; currency = amount*exchangeRate; break; case "F=>$": exchangeRate=1.16; currency = amount*exchangeRate; break; case "F=>€": exchangeRate=1.07; currency = amount*exchangeRate; break; case "¥=>£": exchangeRate=0.0051; currency = amount*exchangeRate; break; case "¥=>$": exchangeRate=0.0067; currency = amount*exchangeRate; break; case "¥=>€": exchangeRate=0.0061; currency = amount*exchangeRate; break; case "¥=>F": exchangeRate=0.0057; currency = amount*exchangeRate; break; case "$=>£": exchangeRate=0.83; currency = amount*exchangeRate; break; case "$=>€": exchangeRate=0.92; currency = amount*exchangeRate; break; case "$=>¥": exchangeRate=149.97; currency = amount*exchangeRate; break; case "$=>F": exchangeRate=0.87; currency = amount*exchangeRate; break; case "£=>$": exchangeRate=1.30; currency = amount*exchangeRate; break; case "£=>€": exchangeRate=1.20; currency = amount*exchangeRate; break; case "£=>¥": exchangeRate=195.30; currency = amount*exchangeRate; break; case "£=>F": exchangeRate=1.12; currency = amount*exchangeRate; break; case "€=>$": exchangeRate=1.09; currency = amount*exchangeRate; break; case "€=>£": exchangeRate=0.83; currency = amount*exchangeRate; break; case "€=>¥": exchangeRate=162.65; currency = amount*exchangeRate; break; case "€=>F": exchangeRate=0.93; currency = amount*exchangeRate; break; } amount=currency; // new amount is value in currency... return currencySymbol+format.format(currency); // new currency //System.out.println("This is your new currency: " +currencySymbol+ currency); } //basic validation if incorrect value(s) for sample(r) and objects(n) public String checkSelection(int n, int r) { if (r==1 || n==1) { return "Sample size (r) or Objects (n) is too small"; } if (r>n) { return "Sample size too big"; } if (r<n && r==1) { return "Sample size too small"; } if (r >totalCurrenciesAvailable || n> totalCurrenciesAvailable) { return "Sample size or objects selected is larger than total currencies available"; } return ""; } //constructor - self explanatory public exchangeCombinations(long combinations, int n, int r) { this.combinations=combinations; this.n=n; this.r=r; totalCurrenciesAvailable=lst.size(); // original size of list, currencies available. System.out.println("Combinations: " + combinations +"\n"); //number combinations without replacement System.out.println("Sample size (r): " + r); System.out.println("Objects: (n): " + n); //Exits if error in input if (checkSelection(n, r)!="") { System.out.println(checkSelection(n, r)); System.exit(0); } //loop will progress until set consists of all nCr combinations... do { System.out.println("\n"); lst = new ArrayList<>(copy); // each time it starts again, it has to get the original ArrayList back temp=""; // this is used concatenation of value before it is stored in the set.... sj=new StringJoiner("=>"); // used for concatenating strings do { // this is crucial part since if the do while loop evaluates as !lst.empty(), this will be suitable if // the sample(r) is the same value as n (objects). Since list consists of n objects. // But if the end user decides to choose combinations of 2 (r) objects out of 3 (n) sample, the while loop // needs adjustment. // alternative boolean statements are required. // currently statement in end of do while is: // !lst.isEmpty() - this is valid for full list // lst.size() >=(n-r) is required if r is less than n exitCondition = !lst.isEmpty(); // this is default evaluation, irrespective of r and n. It will get overriden accordingly //initially exitCondition is true (list.isEmpty() = false... NOT (false) evaluates as true // then exitCondition is false (list.isEmpty()=true....... NOT (true) evaluates as false // FOR SOME REASON, NOT ENTIRELY SURE I HAD TO INCLUDE THIS TO BREAK OUT OF THE DO WHILE LOOP //HOWEVER IT FIXED ISSUE WHICH WAS MAIN OUTCOME..... //IT CAN BE SEEN THAT EXITCONDITION ALWAYS EVALATED AS TRUE (SHOWING LIST NOT EMPTY`) // This is detrimental if the list is empty since there can be no selection // SO I have just broken out of the do while loop with exit condition above..... // This was not needed when n and r were both the same.... //System.out.println("current value boolean: " + exitCondition); if (lst.size()==0) { break; } //System.out.println("Size of list: " + lst.size()); //size list randomNumber = rand.nextInt(lst.size()); //random number between 0 - (size list-1) //System.out.println("random number: " + (randomNumber)); temp=currencySymbol; // this is needed so that the correct currency1=>currency2 symbol can be // examined in switch statement. currencySymbol = lst.get(randomNumber); //gets number from the list conversion = temp + "=>" + currencySymbol; // this is the chain of conversion if (temp!="") // if currencies are not the same, it will start exchange rates // this is just clarifying there are two currencies minimum to be processed { //this is done for a visual check to ensure there are no errors in inputs.... //this can be disabled if required.... //randomTwoCurrencyChecker(); } //System.out.println("This is currency in list: " + currencySymbol); //corresponding value in list // This is now creating Symbol => symbol => Symbol (up to r sample) sj.add(currencySymbol); //temp = temp + Integer.toString(currencySymbol); // concatenating the values lst.remove(randomNumber); // this is important step.. it removes value at this index from list... enforce combination without replacment //System.out.println("WHAT IS LIST SIZE RIGHT NOWWWWW: " + lst.size()); //****************************************** if (n>r) // if end user has selected a smaller sample from n objects { //System.out.println("$£$£$FDSFSDF HERE ! HERE"); //System.out.println(lst.size()); // if n is taken to be smaller also than the currencies available in (lst) // then the lst size also needs to be shrunk to accomodate this... if (n<totalCurrenciesAvailable) { //System.out.println("THIS SHOULD NOT PRINT!!!!!"); // there are this many less objects (n) than total currencies available differenceObjectsAndTotalCurrenciesAvailable = totalCurrenciesAvailable-n; //System.out.println(lst.size()); //System.out.println(differenceObjectsAndTotalCurrenciesAvailable); //System.out.println(n); //System.out.println(r); // this would be true at initial execution // since lst.size() is greater than n objects by differenceObjectsAndTotalCurrenciesAvailable //comparison has to be done on this size // it checks if this smaller list (regarded to be the new n) // is greater than list size after number times(r) a currency has been removed from the list (n) // since do while loop validate condition on exit... // it has to break out to ensure that the currency chain consists of r symbols only..... if ((lst.size()-differenceObjectsAndTotalCurrenciesAvailable>(n-r))) { //System.out.println("EVER GET HERE!!!!!"); //do while loop will continue exitCondition = true; } else { //System.out.println("Ending first do while loop"); break; } } // this now relates to scenario if n is greater than r //n is now equal to totalCurrenciesAvailable (entire list). // This is much easier to follow now... // Since the entire list size is considered to be n... // Once r sample elements removed, it has to break out.... to ensure // r elements are in the conversion chain if (lst.size()>n-r) // this would be true at initial execution { exitCondition = true; //break; } else { //System.out.println("Ending first do while loop"); // //exitCondition = false; break; } } //We know that n is less than totalCurrenciesAvailable //But it will not enter above conditions, since n is also equal to r //This gets slightly trickier again, but is required... //otherwise scenarios such as r=2 n=2 with higher totalCurrenciesAvailable // will process totalCurrenciesAvailable x conversions.... if (n==r) { differenceObjectsAndTotalCurrenciesAvailable = totalCurrenciesAvailable-n; //System.out.println("HERE!!!!!"); //System.out.println(lst.size()); //System.out.println("n:" + n); //System.out.println(differenceObjectsAndTotalCurrenciesAvailable); //now the break will occur if occur if n objects have been processed from the list. // This is as oppose to n-r (since both are identical). // There should be differenceObjectsAndTotalCurrenciesAvailable elements remaining in the // list.. once it has reached this, it needs to break out... if (lst.size()>differenceObjectsAndTotalCurrenciesAvailable) { //System.out.println("EVER GET HERE!!!!!"); // No need to change the exit point //instead best to break out since its processed r elements from n objects exitCondition = true; //break; } else { //System.out.println("Ending first do while loop"); // //exitCondition = false; break; } } } while (exitCondition); //it will keep processing this currentSetSize= s.size(); System.out.println("\n\n************This is current set size: " + currentSetSize+"***********************************"); s.add(sj.toString()); // adding the value to the set. //it performs this check since if the set has not grown, there is no point // in doing arbitration check / screen outputs, since it already in the set System.out.println("************This is new set size: " + s.size()+"***********************************"); //System.out.println("set size: " + s.size()); //once this has incremented, the set has grown... System.out.println("This is saved in the set: " + sj.toString() +"\n"); // Arbitrage check here fullConversions = sj.toString(); System.out.println("******Arbitrage full check - full combination of currencies: " + fullConversions); //so a condition is created as below to ensure it does not perform method call again: if (s.size()==currentSetSize) { System.out.println("*********NOT REQUIRED******************************"); } else { // this method checks two currencies... It is not really a requirement, but it was used initially // before method before developed.... // it is goood to keep as visual check to ensure no major mistakes in input // it also keeps lots of implementation code in this method.... // in order to derive the full conversion chain..... arbitrageCheckerFullCombinationForward(); //this provides detailed breakdown of amounts and arbitration check...... arbitrageCheckerFullCombinationReverse(); } count++; // this is indication of a cycle.. It is aiming to be close to combinations... } while (s.size()<combinations); // this can also be less than or equal to. It does not matter with //do while loop System.out.println("\nNumber cycles: " + count); // based on random seed and filling of set. It can be //any value greater or equal to the combinations....... System.out.println("Original list: " + copy.toString()); //original list outputted to the screen } public void randomTwoCurrencyChecker() { System.out.println("***RANDOM TWO CURRENCY CHECKER************"); System.out.println("Current amount: " + temp + format.format(amount)); // the chain and converted to are passed into the method System.out.println("This is your new currency: " + conversion + " => " + currencyExchange(conversion,currencySymbol)); // the string in reverse... Note it can not be done with reverse function // since the " =>" will become ">=" String reverseConversion = conversion.charAt(3) + "=>" + conversion.charAt(0); //System.out.println("This is reverse76: " + reverseConversion); //System.out.println("temp: " + temp); //Same as above but processes currencies in reverse order System.out.println("This is conversion back to original to check arbitrage: " + reverseConversion + " =>" + currencyExchange(reverseConversion,temp)); System.out.println("***********************"); } public void arbitrageCheckerFullCombinationForward() { System.out.println("\n*****FULL FORWARD CONVERSION****"); //System.out.println("Number times conversion to check is: " + (copy.size()+1)); k=4; // this is set to 4 since $=£ in substring will require exclusion.... startPos=0; endPos = startPos+4; exchangeArbitrage = fullConversions.substring(0,endPos); // as described... amount=50; // this will be the starting point. It makes no difference of the amount. // unless there is consideration for transaction fees. The amount has to be consistent //when doing reverse check.... initialAmount = exchangeArbitrage.charAt(0) + format.format(amount); //gets symbol and amount in 2dp System.out.println("Current amount (AMOUNT SHOULD RETURN HERE IF NO ARBITRAGE): " + initialAmount); //it performs operation for size of sample (r) for (int i=0; i<r; i++) { newCurrency = Character.toString(exchangeArbitrage.charAt(3)); //zero index.. new char will be fourth character $=>£ //System.out.println("segment: " + exchangeArbitrage); //this calls the switch statement in method currencyExchange System.out.println("This is your new currency: " + exchangeArbitrage + " => " + currencyExchange(exchangeArbitrage, newCurrency)); startPos=endPos-1; // startpos is not previously endposition due to substring endPos=startPos+4; // due substring, 4 is set if (endPos>fullConversions.toString().length()) // if no more currencies in chain { break; } exchangeArbitrage = fullConversions.substring(startPos, endPos); //create substring with offsets above //} } System.out.println("**************"); } public void arbitrageCheckerFullCombinationReverse() { System.out.println("\n*****FULL REVERSE CONVERSION****"); // This now has to go back to original state // issue is creating the substring in reverse and directions of => will be flipped //easiest starting point is just reversing the string with StringBuilder sb = new StringBuilder(fullConversions); fullConversionsReverse = sb.reverse().toString(); System.out.println("Reverse conversion****: " + fullConversionsReverse); // this is correct // NOTE THIS NOW DEALS WITH charAt and not substring.. hence exactly zero index based startPos=0; endPos = startPos+3; //it is using reverse method so => is >=. Fixed below exchangeArbitrage = fullConversionsReverse.charAt(startPos) + "=>" + fullConversionsReverse.charAt(endPos) ; // this will take 4 chars such as £=>$ //same reasoning as going forward... r is sample size. for (int i=0; i<r; i++) { newCurrency = Character.toString(exchangeArbitrage.charAt(3)); // this is symbol only exchangeArbitrageFlipped = exchangeArbitrage.charAt(0) + "=>" + newCurrency; //existing currency => new currency //System.out.println(exchangeArbitrageFlipped); // this is correct this will give first char => next currency in line //System.out.println("segment'678 : " + fullConversionsReverse.charAt(0)+"=>"+fullConversionsReverse.charAt(3)); // System.out.println("segment'678 : " + exchangeArbitrageFlipped); outcome = currencyExchange(exchangeArbitrageFlipped, newCurrency); // this stores value of new conversion string, calling switch method currencyExchange //outputs string System.out.println("This is your new currency in reverse: " + exchangeArbitrage + " => " + outcome ); startPos=endPos; // since charAt, refers to exact position zero index endPos = startPos+3; //System.out.println("start pos: " + startPos); //System.out.println("end pos: " + endPos); if (endPos>fullConversionsReverse.length()) // this is to ensure no outbound exception..... { break; } //System.out.println(fullConversionsReverse.charAt(startPos)); //the next conversion of currencies is being prepared in a string exchangeArbitrage = fullConversionsReverse.charAt(startPos) + "=>" + fullConversionsReverse.charAt(endPos); //startPos=startPos+3; // this is correct //endPosition=startPos+k; // this is correct 1 extra char due to exclusion... //exchangeArbitrage = fullConversions.substring(startPos, endPos); // this is correct //it can only fail if it comes out of loop for startpos to reset... but this does not happen... //System.out.println("Crash point next conversion: " + exchangeArbitrage); //System.out.println("Revised string!!!!!: " + exchangeArbitrage); } System.out.println("*********"); //method which checks for arbitration checkForwardWithReverseConversion(); } public void checkForwardWithReverseConversion() { System.out.println("\n**** FINAL CHECK - ARBITRAGE *******"); //difference between outcome and initial amount (both will be same currency) ArbitrageChecker = Double.parseDouble(outcome.substring(1)) - Double.parseDouble(initialAmount.substring(1)); //if difference it reports difference and also calculation... if (ArbitrageChecker >0) { System.out.println("- - - Arbitrage: - - - " + outcome.charAt(0) + format.format(ArbitrageChecker) + " (" + outcome + " - " + initialAmount+")"); } //simple statement if no difference... else { System.out.println("- - - NO Arbitrage: - - - "); } System.out.println("***********************************************************************"); } // end of method } public class Combination { public static void main(String[] args) { System.out.println("Welcome to Online IDE!! Happy Coding :)"); int originalNumber=5; int n=originalNumber; int r =3; Map <Integer, Long> m = new HashMap<>(); System.out.println("***COMBINATIONS*** (WITHOUT REPLACEMENT)"); System.out.println("C(n,r) = n! / (r!(n−r)!)"); System.out.println("C(" + n+","+r+") = " + n+"!" + " / " + "("+r+"!"+"("+n+"-"+r+")!)"); exchangeCombinations ec = new exchangeCombinations(Combinations (n,r,originalNumber, m), n,r); } public static long Combinations (int n, int r, int originalNumber, Map factorialResults) { // n are objects // r is sample /* ***CALCULATION*** P(n,r) = n! / (r!(n−r)!) */ long result=0; int denominator1; int denominator2; if (n>=1) { // EXAMPLE // P (5,6) = 6* 5* 4 * 3 * 2 * 1 / 6! (6-5)! = 720 / (5! * 1!) = 120 / 5*4*3*2*1 * 1 = 720 / 120 = 6 result = (n* (Combinations (n-1, r,originalNumber, factorialResults))); // this completes factorial for numerator factorialResults.put(n,result); //result stored in the Map //System.out.println("getting result back out: " + factorialResults.get(n)); if (n==originalNumber) // this will occur once { denominator1 = originalNumber-r; // originalNumber required since n has reduced as part of the recursive calls denominator2 = r; // r sample size has not changed // this is using the Java Memoization technique to ensure the factorial outcome is not calculated again, to save program cycles. // since the returns are done in reverse order.... n = 1 is processed first and n=6 last... Hence in practice // there will be entry in Map for all factorials, ready for the denominator.. if (factorialResults.containsKey(denominator1) && factorialResults.containsKey(denominator2)) { //System.out.println("here"); //System.out.println("This is exact value of factorial " + (denominator1) + " : " + factorialResults.get(denominator1)); //System.out.println("This is exact value of factorial " + (denominator2) + " : " + factorialResults.get(denominator2)); return result / ((long) factorialResults.get(denominator1) * (long)factorialResults.get(denominator2)); } } return result; } return 1; // it should not reach here } }